package com.spring;

import com.spring.anotation.AutoWired;
import com.spring.anotation.Component;
import com.spring.anotation.ComponentScan;

import com.spring.anotation.Scope;
import com.spring.exception.ContextException;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.*;

public class XLApplicationContext {
    private Class configClass;
    private final List<String> classNameList = new ArrayList<String>();

    private final Map<String, Object> beanMap = new HashMap<>();
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    public XLApplicationContext(Class configClass) {
        System.out.println("================================");
        this.configClass = configClass;

        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        scanPackage(path);
//        classNameList.forEach(System.out::println);

        excuteBeanDefinition();
        // 实例化bean，并保存
        excuteInstance();
        // TODO 自动注入
//        excuteAutoWired();
    }

    private void excuteAutoWired() {

    }

    private void excuteBeanDefinition() {
        classEmptyCheck();
        try {
            for (String aClassName : classNameList) {
                Class<?> clazz = Class.forName(aClassName);
                if (clazz.isAnnotationPresent(Component.class)) {
                    Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                    String beanName = componentAnnotation.value();
                    // 注解没赋值，按接口或类名映射
                    if ("".equals(beanName)) {
                        Class<?>[] interfaces = clazz.getInterfaces();
                        // 有接口按接口名映射
                        if (interfaces.length > 0) {
                            Class<?> anInterface = interfaces[0];
                            beanName = StringUtils.uncapitalize(anInterface.getSimpleName());
                        } else {
                            // 无接口按类名映射
                            beanName = StringUtils.uncapitalize(clazz.getSimpleName());
                        }
                    }

                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(clazz);
                    if (clazz.isAnnotationPresent(Scope.class)) {
                        Scope scope = clazz.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(scope.value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }
                    beanDefinitionMap.put(beanName, beanDefinition);
                }

                // UserController -> userController
//                if (clazz.isAnnotationPresent(Controller.class)) {
//                    String beanName = StringUtils.uncapitalize(clazz.getSimpleName());
//                    // 映射bean  userController->controller实例
//                    beanMap.put(beanName, clazz.getDeclaredConstructor().newInstance());
//                } else if (clazz.isAnnotationPresent(Service.class)) {
//                    Service annotation = clazz.getAnnotation(Service.class);
//                    String beanName = annotation.value();
//                    // 注解没赋值，按接口或类名映射
//                    if ("".equals(beanName)) {
//                        Class<?>[] interfaces = clazz.getInterfaces();
//                        // 有接口按接口名映射
//                        if (interfaces.length > 0) {
//                            Class<?> anInterface = interfaces[0];
//                            beanName = StringUtils.uncapitalize(anInterface.getSimpleName());
//                        } else {
//                            // 无接口按类名映射
//                            beanName = StringUtils.uncapitalize(clazz.getSimpleName());
//                        }
////                        for (Class<?> anInterface : interfaces) {
////                            beanName = anInterface.getSimpleName().substring(0, 1).toLowerCase(Locale.ROOT)
////                                    + anInterface.getSimpleName().substring(1);
////                            beanMap.put(beanName, anInterface.getDeclaredConstructor().newInstance());
////                        }
//                    }
//                    beanMap.put(beanName, clazz.getDeclaredConstructor().newInstance());
//                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void scanPackage(String aPackage) {
        // 将com.xl转换为/com/xl
//        URL resource = this.getClass().getClassLoader().getResource("/" + aPackage.replaceAll("\\.", "/"));
        ClassLoader classLoader = XLApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(aPackage.replaceAll("\\.", "/"));

        assert resource != null;
        String path = resource.getFile();
        File dir = new File(path);
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if(file.isDirectory()) {
//                System.out.println(aPackage + "." + file.getName());
                scanPackage(aPackage + "." + file.getName());
            } else {
                classNameList.add(aPackage + "." + file.getName().replaceAll(".class", ""));
            }
        }
    }

    private void beanEmptyCheck() {
        if (beanMap.isEmpty()) {
            throw new ContextException("bean 空");
        }
    }

    private void classEmptyCheck() {
        if (classNameList.size() == 0) {
            throw new ContextException("没有扫描到实例化类");
        }
    }

    private Object createBean(BeanDefinition beanDefinition) {
        Object o = null;
        try {
            o = beanDefinition.getClazz().getDeclaredConstructor().newInstance();

//            自动注入先后顺序问题？
//            Field[] declaredFields = o.getClass().getDeclaredFields();
//            for (Field declaredField : declaredFields) {
//                if (declaredField.isAnnotationPresent(AutoWired.class)) {
//                    // TODO 此处需完善，若fieldName未查到，根据类名、接口名进行查找
//                    String name = declaredField.getName();
//                    declaredField.setAccessible(true);
//                    declaredField.set(o, getBean(name));
//                }
//            }

        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }

    private void excuteInstance() {
        for (Map.Entry<String, BeanDefinition> stringBeanDefinitionEntry : beanDefinitionMap.entrySet()) {
            BeanDefinition beanDefinition = stringBeanDefinitionEntry.getValue();
            String beanName = stringBeanDefinitionEntry.getKey();
            if (beanDefinition.getScope().equals("singleton")) {
                beanMap.put(beanName, createBean(beanDefinition));
            }
        }

    }

    public Object getBean(String beanName) {
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                return beanMap.get(beanName);
            } else {
//                创建bean
//                Class clazz = beanDefinition.getClazz();
                return createBean(beanDefinition);
            }
        } else {
            throw new ContextException("不存在对应的bean");
        }
    }

}
